using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Chan_cli.Modules;

namespace Chan_cli
{
    public partial class frmDebug : Form
    {
        SrvHooks sh;
        string oldIP;
        int oldPort;

        SidebarModule Side;
        FilesModule Files;
        RootModule Root;

        public frmDebug()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Kills the 'sh' instance of SrvHooks stone dead.  Alas, C#'s garbage collection is not as
        /// speedy and near-instant as we'd wish...
        /// </summary>
        private void Killsh()
        {
            // Alas, C#'s garbage collection is not fast enough for us.
            // It's more of an occasional thing, and if the thread is left running, the reference
            // is never killed...
            // So, we need to do a few things to make sure it's utterly dead.

			if (sh != null)
			{
	            // Signal ending of thread (so references aren't kept in motion)
	            sh.KillBackgroundThread();

	            // Kill the last reference to SrvHooks
	            sh = null;

	            // Now, force a garbage collection...
	            GC.Collect();
            }
        }

        /// <summary>
        /// Not called on an Application.Exit(), when the user clicks the X button on the debug window this
        /// stops the whole program from closing.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">The CancelEventArgs that will have it's Cancel property modified.</param>
        private void frmDebug_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // Not called on Application.Exit() ...
            e.Cancel = true;
            this.Hide();
        }

        /// <summary>
        /// Creates a new (SrvHooks) sh instance using the current settings, and updates the
        /// oldIP and oldPort local variables.
        /// </summary>
        private void Startsh()
        {

            // For now, this goes here. We need central module loading place.
            Side = new SidebarModule("main");
            SidebarCategory Cat = new SidebarCategory("Boards", false, true);
                Cat.AddLink("b", "/b/ - Random", "/board/b/", true);
                Cat.AddLink("s", "/s/ - Superman", "/board/s/", true);
                Cat.AddLink("r", "/r/ - Request", "/board/r/", true);
                Side.AddCategory("Boards", Cat);
            Files = new FilesModule(Properties.Settings.Default.FilesDir);
            Root = new RootModule();

            // HACK
            XHTMLTemplate.FilesDir = Properties.Settings.Default.FilesDir;

            try
            {
                sh = new SrvHooks(Properties.Settings.Default.ListenIP, Properties.Settings.Default.ListenPort);

                sh.AddModule("sidebar", Side);
                sh.AddModule("files", Files);
                sh.AddModule("", Root);
            }
            catch (Exception e)
            {
                MessageBox.Show("Error occured in starting server; ('" + e.Message + "', caused by '" + e.Source + "')");

                // Dump exception to disk...

            }

            oldIP = Properties.Settings.Default.ListenIP;
            oldPort = Properties.Settings.Default.ListenPort;

            UpdateNotifyIcon();
        }

        /// <summary>
        /// Destroys and then creates a new (SrvHooks) sh using the current settings stored.
        /// </summary>
        private void Restartsh()
        {
            Killsh();
            Startsh();
        }

        /// <summary>
        /// (TODO in part) Updates the notify icon with the current network, as well as the IP and port the
        /// local HTTP server is listening on...
        /// </summary>
        private void UpdateNotifyIcon()
        {
            notifyIcon.Text = "Chan-cli: On network (???), serving on " + Properties.Settings.Default.ListenIP + ":" + Properties.Settings.Default.ListenPort.ToString();
        }


        /// <summary>
        /// Loads in the initial UI settings, and starts the SrvHooks instance that gets everything going.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void frmDebug_Load(object sender, EventArgs e)
        {
            txtArchiveDir.Text = Properties.Settings.Default.ArchiveDir;
            txtFilesDir.Text = Properties.Settings.Default.FilesDir;
            txtCacheDir.Text = Properties.Settings.Default.CacheDir;

            txtListenIP.Text = Properties.Settings.Default.ListenIP;
            txtListenPort.Text = Properties.Settings.Default.ListenPort.ToString();
            
            txtDirectoryServerAddress.Text = Properties.Settings.Default.DirectoryServerIP;
            txtDirectoryServerPort.Text = Properties.Settings.Default.DirectoryServerPort.ToString();
            
            txtLaunchCmd.Text = Properties.Settings.Default.BrowserLaunchCmd;
            
            txtP2PPort.Text = Properties.Settings.Default.P2PPort.ToString();
            cmbBandwidth.SelectedIndex = (int)Properties.Settings.Default.P2PBandwidthLevel;

            Startsh();
        }

        private void cmdUpdateDirs_Click(object sender, EventArgs e)
        {
            if (System.IO.Directory.Exists(txtArchiveDir.Text))
                Properties.Settings.Default.ArchiveDir = txtArchiveDir.Text;
            else
                throw new Exception("Archive directory does not appear to be valid!");

            if (System.IO.Directory.Exists(txtCacheDir.Text))
                Properties.Settings.Default.CacheDir = txtCacheDir.Text;
            else
                throw new Exception("Cache directory does not appear to be valid!");

            if (System.IO.Directory.Exists(txtFilesDir.Text))
                Properties.Settings.Default.FilesDir = txtFilesDir.Text;
            else
                throw new Exception("Files directory does not appear to be valid!");

            Properties.Settings.Default.Save();

            // Tell it to read in the new settings...
            sh.NotifyDirectoryUpdate();

        }

        /// <summary>
        /// Handles the 'Restart HTTP Server (with new settings)' button press.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void cmdRestartHTTPServer_Click(object sender, EventArgs e)
        {
            try
            {
                int.Parse(txtListenPort.Text);
            }
            catch (Exception w)
            {
                throw new Exception("ListenPort could not be parsed as an integer!", w);
            }

            Properties.Settings.Default.ListenIP = txtListenIP.Text;
            Properties.Settings.Default.ListenPort = int.Parse(txtListenPort.Text);
            Properties.Settings.Default.Save();

            if ((oldIP != Properties.Settings.Default.ListenIP) || (oldPort != Properties.Settings.Default.ListenPort))
            {
                // Recreate server.
                Restartsh();
            }
            else
                MessageBox.Show("This action is unnecessary; you've changed nothing!", "Redundancy Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            LaunchSelfInBrowser();
        }

        private void notifyIcon_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
                this.Show();
        }

        private void mnuIconLaunchInBrowser_Click(object sender, EventArgs e)
        {
            LaunchSelfInBrowser();
        }

        public void LaunchSelfInBrowser()
        {
        	// FIXME: This fails on non-Windows.  Let us choose the launch command
        	// from the UI?
            System.Diagnostics.Process.Start("http://127.0.0.1:" + Properties.Settings.Default.ListenPort.ToString() + "/");
        }

        /// <summary>
        /// Handles the {icon context menu}->'Open Debug Window' button press.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void mnuIconOpenDebugWindow_Click(object sender, EventArgs e)
        {
            this.Show();
        }

        /// <summary>
        /// Handles the {icon context menu}->'Exit' button press.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void mnuIconQuit_Click(object sender, EventArgs e)
        {
            sh.KillBackgroundThread();
            sh = null;
            System.Threading.Thread.Sleep(1000);
            GC.Collect();
            Application.Exit();
        }

        /// <summary>
        /// Handles the 'Simulate Icon->Exit' button press.
        /// HACK: We can probably do this directly
        /// </summary>
        /// <param name="sender">Sender (not used)</param>
        /// <param name="e">Parameters (not used)</param>
        public void cmdSimIconExit_Click(object sender, EventArgs e)
        {
            mnuIconQuit_Click(sender, e);
        }

        /// <summary>
        /// Handles the 'Simulate Dump Exception' button press.
        /// Do we still have this thing??
        /// </summary>
        /// <param name="sender">Sender (not used)</param>
        /// <param name="e">Parameters (not used)</param>
        private void cmdSimDumpException_Click(object sender, EventArgs e)
        {
            try
            {
                int z = 1;
                z -= 1;
                System.Console.WriteLine((int)3 / z);
            }
            catch (Exception ex)
            {
                Chanutils.HELP.DumpException(ex);
            }
        }



        /// <summary>
        /// Function helper for the cache, archive, and /files directory fields'
        ///  'Browse...' buttons
        /// </summary>
        /// <param name="tb"></param>
        private void PathBrowse(ref TextBox tb)
        {
            dlgFolderBrowse.SelectedPath = tb.Text;

            if (dlgFolderBrowse.ShowDialog() == DialogResult.OK)
            {
                tb.Text = dlgFolderBrowse.SelectedPath;
            }
        }

        /// <summary>
        /// Handles the /files directory field 'Browse...' button press
        /// </summary>
        /// <param name="sender">Who sends? (not used)</param>
        /// <param name="e">Parameters? (not used)</param>
        private void cmdFilesDirBrowse_Click(object sender, EventArgs e)
        {
            PathBrowse(ref txtFilesDir);
        }
        
        /// <summary>
        /// Handles the cache directory field 'Browse...' button press
        /// </summary>
        /// <param name="sender">Who sends? (not used)</param>
        /// <param name="e">Parameters? (not used)</param>
        private void cmdCacheDirBrowse_Click(object sender, EventArgs e)
        {
            PathBrowse(ref txtCacheDir);
        }

        /// <summary>
        /// Handles the archive direcotry field 'Browse...' button press
        /// </summary>
        /// <param name="sender">Who sends? (not used)</param>
        /// <param name="e">Parameters? (not used)</param>
        private void cmdArchiveDirBrowse_Click(object sender, EventArgs e)
        {
            PathBrowse(ref txtArchiveDir);
        }

        /// <summary>
        /// Handles the 'Help' button press in the log 'Save As...' dialog
        /// HACK:  Replace this with a HTML page!
        /// </summary>
        /// <param name="sender">Who sends? (not used)</param>
        /// <param name="e">Parameters? (not used)</param>
        private void dlgLogSave_HelpRequest(object sender, EventArgs e)
        {
        	MessageBox.Show("It's really quite simple.\r\n\r\n - If your text editor is dysfunctional or just stupid (e.g. MS-DOS 'EDIT'), you need ASCII.\r\n - If you have weird and wonderful characters in the log (double-byte character languages, Not English) then you need Unicode.\r\n - If you have neither of these requirements, use either.", "Help", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
        
        private void cmdSaveLogAs_Click(object sender, EventArgs e)
        {
        	if (dlgLogSave.ShowDialog() == DialogResult.OK)
        	{
        		try
        		{
        			// Bind the 'enumerator' object into an array (kinda) of strings
        			IEnumerator<string> ieLogging = (IEnumerator<string>) lstLog.Items.GetEnumerator();
        			
        			// Er. HACK! (jesus christ this code sucks ass)
        			// FIXME: We're not reading the filetype to see if we should be outputting UTF8 or ASCII atm
        			string[] aRr = new string[lstLog.Items.Count];

        			for (int i = 0; i <= aRr.Length - 1; i++)
        			{
        				aRr[i] = ieLogging.Current;
        				ieLogging.MoveNext();
        			}
        			
        			System.IO.File.WriteAllLines(dlgLogSave.FileName, aRr);
        		}
        		catch (Exception ex)
        		{
        			Chanutils.HELP.DumpException(false, ex);
        			MessageBox.Show("Log save failed! (" + '"' + ex.Message + '"' + ").", "Fail", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);       			
        		}
        	}
        }
        
        private void cmdClearLog_Click(object sender, EventArgs e)
        {
        	lstLog.Items.Clear();
        }
        
        void ChkCanDeleteFromCacheCheckedChanged(object sender, EventArgs e)
        {
        	// TODO: Implement ChkCanDeleteFromCacheCheckedChanged
        }
        
        private void chkCanDeleteFromCache_MouseClick(object sender, MouseEventArgs e)
        {
        	if (e.Button == MouseButtons.Right)
        		InformationalMessage("This controls whether items are ever automatically removed from the cache.\r\n\r\nItems are normally removed due to:\r\n  - Your local peers voting it for deletion due to rule violations\r\n  - Age\r\n  - (For threads) being updated for more posts\r\n  - Disk space restrictions (to stop Chan-cli from consuming all of your hard disk)\r\n\r\nThis option REALLY should be used only for debugging purposes.  Crap builds up FAST.");
        }
        
        private void InformationalMessage(string Message)
        {
        	MessageBox.Show(Message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
                
        private void chkTrustFriendsForVote_MouseClick(object sender, MouseEventArgs e)
        {
        	if (e.Button == MouseButtons.Right)
        		InformationalMessage("This controls whether or not you listen to any of your friend peers' votes for deletion, and votes for sticky.\r\n\r\nVotes for deletion are useful if you aren't constantly refreshing the boards, all the time, while the application is running, as it means you can trust your friends to help identify illegal content and keep it off your computer.\r\n\r\nVotes for sticky are just kind of cool.\r\n\r\nObviously, with this option off a lot of shit you may not otherwise see can build up on your computer!");
        }
        
        private void chkAlwaysHashUnfresh_MouseClick(object sender, MouseEventArgs e)
        {
        	if (e.Button == MouseButtons.Right)
        		InformationalMessage("If this is checked, when the client specifically requests a certain thread or binary chunk off one friend, we ask for the hashes of the text/data from our other friends to make sure everybody has the same idea of what the data should be.\r\n\r\nTurning this off may provide better load times, but any peer can spoof data without you noticing.\r\n(TODO: Remove this before 1.0 release!  If the ever-delusional users start turning this off, they get spoofed data and inadvertedly lie to all their friend peers, too!  (Network == VERY SHIT then))");
        } 
        
        private void chkDontHashFresh_MouseClick(object sender, MouseEventArgs e)
        {
        	if (e.Button == MouseButtons.Right)
        		InformationalMessage("If this is checked (default), we don't hash any content that is delivered via NOTIFY commands to us.\r\n\r\nAn obvious security risk, we should obviously come up with some kind of compromise.\r\nThe problem is, if we try to get hashes immediately it's possible that we're one of the first, if not THE first peer to have it.\r\nIf we decide to wait until other people have it, nobody will ever get it because we haven't propogated the content further on.\r\n\r\nWe'll have to see how this works out.");
        }
        
        private void cmdApplyBrowserSettings_Click(object sender, EventArgs e)
        {
        	Properties.Settings.Default.BrowserLaunchCmd = txtLaunchCmd.Text;
        	Properties.Settings.Default.Save();
        }
        
        private void cmdP2PRestart_Click(object sender, EventArgs e)
        {
        	// TODO: Restart stuff D=
        	
        	
        	
        	Properties.Settings.Default.DirectoryServerIP = txtDirectoryServerAddress.Text;
        	
        	try
        	{
        		Properties.Settings.Default.DirectoryServerPort = int.Parse(txtDirectoryServerPort.Text);
        	}
        	catch (Exception ex)
        	{
        		MessageBox.Show("Failed to parse 'Directory Server - Port' to integer.\r\n\r\n'" + ex.Message + "'", "Invalid Port", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        		Chanutils.HELP.DumpException(false, ex);
        	}
        	
        	try
        	{
        		Properties.Settings.Default.P2PPort = int.Parse(txtP2PPort.Text);
        	}
        	catch (Exception ex)
        	{
        		MessageBox.Show("Failed to parse 'Peer-to-Peer - Listen Port' to integer.\r\n\r\n'" + ex.Message + "'", "Invalid Port", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        		Chanutils.HELP.DumpException(false, ex);
        	}
        	
        	Properties.Settings.Default.P2PBandwidthLevel = (byte)cmbBandwidth.SelectedIndex;
        	
        	
        	// Aaaand save. ^^
        	Properties.Settings.Default.Save();
        }
    }
}

